package com.company.nuwa.mybatis.generator.plugins;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.OutputUtilities;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.XmlConstants;
import org.mybatis.generator.codegen.mybatis3.MyBatis3FormattingUtilities;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.util.StringUtility;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.stream.Collectors;

/**
 * <p>Ext</p>
 *
 * @author Lionel Lee
 * @version 1.0: MySQLLimitPlugin.java
 * @date 2021/5/4  16:49 星期二
 */
public class MySQLLimitPlugin extends PluginAdapter {

    private static final String XMLFILE_POSTFIX = "Ext";

    private static final String JAVAFILE_POTFIX = "Ext";

    private static final String ANNOTATION_RESOURCE = "org.apache.ibatis.annotations.Mapper";

    @Override
    public boolean validate(List<String> list) {
        return true;
    }

    // 生成XXExt.xml
    @Override
    public List<GeneratedXmlFile> contextGenerateAdditionalXmlFiles(IntrospectedTable introspectedTable) {

        String[] splitFile = introspectedTable.getMyBatis3XmlMapperFileName().split("\\.");
        String fileNameExt = null;
        if (splitFile[0] != null) {
            fileNameExt = splitFile[0] + XMLFILE_POSTFIX + ".xml";
        }

        if (isExistExtFile(context.getSqlMapGeneratorConfiguration().getTargetProject(),
                introspectedTable.getMyBatis3XmlMapperPackage(), fileNameExt)) {
            return super.contextGenerateAdditionalXmlFiles(introspectedTable);
        }

        Document document = new Document(XmlConstants.MYBATIS3_MAPPER_PUBLIC_ID,
                XmlConstants.MYBATIS3_MAPPER_SYSTEM_ID);

        XmlElement root = new XmlElement("mapper");
        document.setRootElement(root);
        String namespace = introspectedTable.getMyBatis3SqlMapNamespace() + XMLFILE_POSTFIX;
        root.addAttribute(new Attribute("namespace", namespace));
        createResultMap(introspectedTable, root);
        //
        String columns = introspectedTable.getAllColumns().stream()
                .map(MyBatis3FormattingUtilities::getEscapedColumnName)
                .collect(Collectors.joining(","));
        String sql = "<sql id=\"Base_Column_Ext_List\">" + columns + "  </sql>";
        root.addElement(new TextElement(sql));
        GeneratedXmlFile gxf = new GeneratedXmlFile(document, fileNameExt,
                introspectedTable.getMyBatis3XmlMapperPackage(),
                context.getSqlMapGeneratorConfiguration().getTargetProject(), false,
                context.getXmlFormatter());

        List<GeneratedXmlFile> answer = new ArrayList<GeneratedXmlFile>(1);
        answer.add(gxf);

        return answer;
    }

    private void createResultMap(IntrospectedTable introspectedTable, XmlElement root) {
        FullyQualifiedJavaType domainType = new FullyQualifiedJavaType(
                introspectedTable.getBaseRecordType());

        StringBuilder sql = new StringBuilder("<resultMap id=\"BaseResultExtMap\" type=\""
                + domainType.getFullyQualifiedName() + "\">\n");
        OutputUtilities.xmlIndent(sql, 3);
        // 主键
        IntrospectedColumn pkColumn = introspectedTable.getPrimaryKeyColumns().get(0);
        // 公共字段
        // 数据库字段名
        String columnName;
        // java字段名
        String javaProperty;
        for (IntrospectedColumn introspectedColumn : introspectedTable.getAllColumns()) {
            columnName = MyBatis3FormattingUtilities.getEscapedColumnName(introspectedColumn);
            javaProperty = introspectedColumn.getJavaProperty();
            if (pkColumn.getActualColumnName().equals(introspectedColumn.getActualColumnName())) {
                //
                sql.append("  <id column=\"").append(columnName).append("\" jdbcType=\"")
                        .append(introspectedColumn.getJdbcTypeName()).append("\" property=\"")
                        .append(javaProperty).append("\" />\n");
            } else {
                sql.append("  <result column=\"").append(columnName).append("\" jdbcType=\"")
                        .append(introspectedColumn.getJdbcTypeName()).append("\" property=\"")
                        .append(javaProperty).append("\" />\n");
            }

        }
        sql.append("  </resultMap>");
        root.addElement(new TextElement(sql.toString()));

    }

    // 生成XXExt.java
    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {

        FullyQualifiedJavaType type = new FullyQualifiedJavaType(
                introspectedTable.getMyBatis3JavaMapperType() + JAVAFILE_POTFIX);
        Interface interfaze = new Interface(type);
        interfaze.setVisibility(JavaVisibility.PUBLIC);
        context.getCommentGenerator().addJavaFileComment(interfaze);

        FullyQualifiedJavaType baseInterfaze = new FullyQualifiedJavaType(
                introspectedTable.getMyBatis3JavaMapperType());
        interfaze.addSuperInterface(baseInterfaze);

        FullyQualifiedJavaType annotation = new FullyQualifiedJavaType(ANNOTATION_RESOURCE);
        interfaze.addAnnotation("@Mapper");
        interfaze.addImportedType(annotation);

        interfaze.addJavaDocLine("/**");
        String remarks = introspectedTable.getRemarks();
        if (StringUtility.stringHasValue(remarks)) {
            String[] remarkLines = remarks.split(System.getProperty("line.separator"));
            for (String remarkLine : remarkLines) {
                interfaze.addJavaDocLine(" * <p>" + remarkLine + "</p>");
            }
        }

        StringBuilder sb = new StringBuilder();
        sb.append(" * @author ").append(System.getProperties().getProperty("user.name"));
        interfaze.addJavaDocLine(sb.toString());
        sb.setLength(0);
        sb.append(" * ").append("@version 1.0:")
                .append(introspectedTable.getFullyQualifiedTable().getDomainObjectName())
                .append("Mapper").append(".java");
        interfaze.addJavaDocLine(sb.toString());
        sb.setLength(0);
        sb.append(" * @date ");
        sb.append(getDateString());
        interfaze.addJavaDocLine(sb.toString());
        interfaze.addJavaDocLine(" */");

        CompilationUnit compilationUnits = interfaze;
        GeneratedJavaFile generatedJavaFile = new GeneratedJavaFile(compilationUnits,
                context.getJavaModelGeneratorConfiguration().getTargetProject(),
                context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                context.getJavaFormatter());

        if (isExistExtFile(generatedJavaFile.getTargetProject(),
                generatedJavaFile.getTargetPackage(), generatedJavaFile.getFileName())) {
            return super.contextGenerateAdditionalJavaFiles(introspectedTable);
        }
        List<GeneratedJavaFile> generatedJavaFiles = new ArrayList<GeneratedJavaFile>(1);
        generatedJavaFile.getFileName();
        generatedJavaFiles.add(generatedJavaFile);
        return generatedJavaFiles;
    }

    private boolean isExistExtFile(String targetProject, String targetPackage, String fileName) {

        File project = new File(targetProject);
        if (!project.isDirectory()) {
            return true;
        }

        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(targetPackage, ".");
        while (st.hasMoreTokens()) {
            sb.append(st.nextToken());
            sb.append(File.separatorChar);
        }

        File directory = new File(project, sb.toString());
        if (!directory.isDirectory()) {
            boolean rc = directory.mkdirs();
            if (!rc) {
                return true;
            }
        }

        File testFile = new File(directory, fileName);
        return testFile.exists();
    }

    protected String getDateString() {
        return DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
    }

    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        //重新生成代码,xml内容覆盖
        sqlMap.setMergeable(false);
        return super.sqlMapGenerated(sqlMap, introspectedTable);
    }

}